package jinyilw.tools.net;

import jinyilw.common.Operator;
import jinyilw.common.OperatorSets;
import jinyilw.common.log.LogTools;
import jinyilw.tools.net.dev.BaseClient;
import jinyilw.tools.net.dev.CommandPacket;
import jinyilw.tools.net.dev.NetworkMessage;
import jinyilw.tools.net.dev.TCPServer;
import jinyilw.tools.net.file.FileTransferServer;

import java.io.File;
import java.io.IOException;

public class ServerTools implements Runnable
{
	private static volatile ServerTools defaultTools;

	private TCPParameter parameter;
	private TCPServer server;
	private FileTransferServer fileTransferServer;
	private volatile boolean threadRun;
	private final OperatorSets<BaseClient> clientDisconnectOperators =
			new OperatorSets<>(
					true);
	private final OperatorSets<BaseClient> clientConnectingOperators =
			new OperatorSets<>(
					true);
	private final OperatorSets<BaseClient> receiveClientPacketOperators =
			new OperatorSets<>(
					true);

	private boolean enableUpdate;

	public ServerTools(TCPParameter parameter)
	{
		this.parameter = parameter;
		server = NetTools.createTCPServer(parameter);
	}

	public static ServerTools getDefaultTools()
	{
		if (defaultTools == null)
			synchronized (ServerTools.class)
			{
				if (defaultTools == null)
					defaultTools = new ServerTools(
							NetConfig.getDefaultTCPParameter());
			}
		return defaultTools;
	}

	public void addClientDisconnectOperator(Operator<BaseClient> operator)
	{
		clientDisconnectOperators.add(operator);
	}

	public void addClientConnectingOperator(Operator<BaseClient> operator)
	{
		clientConnectingOperators.add(operator);
	}

	public void addReceiveClientPacketOperator(Operator<BaseClient> operator)
	{
		receiveClientPacketOperators.add(operator);
	}

	public void broadcastMessage(String message)
	{
		if (server != null && message != null)
			try
			{
				server.broadcastPacket(new NetworkMessage(message));
			} catch (IOException ex)
			{
				ex.printStackTrace();
			}
	}

	public boolean broadcastCommand(CommandPacket commandPacket, int command)
	{
		if (commandPacket == null)
			return false;
		commandPacket.setCommandID((short) command);
		return broadcastCommand(commandPacket);
	}

	public boolean broadcastCommand(CommandPacket commandPacket)
	{
		if (server != null && commandPacket != null)
			try
			{
				server.broadcastPacket(commandPacket);
				return true;
			} catch (IOException ex)
			{
				ex.printStackTrace();
			}
		return false;
	}

	public TCPServer getServer()
	{
		return server;
	}

	public BaseClient[] getConnectedClients()
	{
		if (server != null)
			return server.getClients();
		return null;
	}

	public int getAllClientsNumber()
	{
		if (server != null)
			return server.getClients().length
					+ server.getConnectingClients().length;
		return 0;
	}

	public int getConnectedClientsLength()
	{
		if (server != null)
			return server.getClients().length;
		return 0;
	}

	public int getConnectingClientsLength()
	{
		if (server != null)
			return server.getConnectingClients().length;
		return 0;
	}

	public void setParameter(TCPParameter param)
	{
		if (parameter != param)
		{
			parameter = param;
			if (threadRun)
			{
				restart();
			}
		}
	}

	public boolean isConnected()
	{
		return server != null;
	}

	public synchronized boolean start()
	{
		if (threadRun)
			return true;
		if (server != null)
		{
			threadRun = true;
			new Thread(this).start();
		}
		return threadRun;
	}

	public synchronized boolean restart()
	{
		stop();
		server = NetTools.createTCPServer(parameter);
		if (fileTransferServer != null)
			fileTransferServer.setTcpServer(server);
		if (server != null)
		{
			threadRun = true;
			new Thread(this).start();
		}
		return threadRun;
	}

	public synchronized void stop()
	{
		threadRun = false;
		if (server != null)
			try
			{
				server.disconnect();
			} catch (IOException e)
			{
				e.printStackTrace();
			} finally
			{
				server = null;
			}
	}

	protected void dealUpdate(BaseClient client)
	{
		UpdateTools.handleUpdateInThread(client);
	}

	public ServerTools enableUpdate()
	{
		enableUpdate = true;
		return this;
	}
	public void disableUpdate()
	{
		enableUpdate = false;
	}

	public synchronized void startFileTransferServer(int port,
			File receiveRootFolder, File sendRootFolder)
	{
		if (fileTransferServer != null)
			fileTransferServer.stop();
		try
		{
			fileTransferServer = new FileTransferServer(port);
			fileTransferServer.setReceiveRootFolder(receiveRootFolder);
			fileTransferServer.setSendRootFolder(sendRootFolder);
			fileTransferServer.setTcpServer(server);
			fileTransferServer.start();
		} catch (IOException e)
		{
			fileTransferServer = null;
			e.printStackTrace();
		}
	}

	@Override
	public void run()
	{
		LogTools.info("TCP服务器启动成功...");
		while (threadRun)
		{
			try
			{
				server.update(200);
			} catch (IOException e)
			{
				server = null;
				threadRun = false;
				LogTools.debug("TCP服务器运行异常!!!", e);
				break;
			}
			if (!threadRun || server == null)
				return;

			for (BaseClient client : server.getConnectingClients())
			{
				LogTools.info("> " + client.getRemoteDetail()
						+ " TCP客户端连接成功... 连接数："
						+ server.getAllClientsNumber());
				clientConnectingOperators.operate(client);
			}
			for (BaseClient client : server.getDisconnectedClients())
			{
				LogTools.info("> " + client.getRemoteDetail()
						+ " TCP客户端断开连接... 连接数："
						+ server.getAllClientsNumber());
				clientDisconnectOperators.operate(client);
			}
			for (BaseClient client : server.getReceivedPacketClients())
			{
				receiveClientPacketOperators.operate(client);
				if (enableUpdate)
					dealUpdate(client);
			}
		}
		LogTools.info("TCP服务器停止运行...");
	}
}
